/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1/commercial
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may use this file in compliance with the License.
 * Alternatively, if you can't copy with the License, namely if you do not
 * want to provide source code of your modifications to this file, you may
 * negotiate a commercial licence with the initial developer.
 * You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is COID.
 *
 * The Initial Developer of the Original Code is
 * PosAm.
 * Portions created by the Initial Developer are Copyright (C) 2003
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 * Brano Kemen
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

#include "serverface.h"
#include "serviceinst.h"
#include "treemgr.h"
#include "server.h"

#include "coid/comm/binstream/inoutstream.h"
#include "coid/comm/binstream/nullstream.h"
#include "coid/coid.h"


namespace coid {

////////////////////////////////////////////////////////////////////////////////
//opcd CoidNode::dpx_predestroy()
//{
//    return dispatch( StdDispatchMethod::PREDESTRUCTOR, nullstream, 0 );
//}

////////////////////////////////////////////////////////////////////////////////
opcd CoidNode::dpx_destroy()
{
    dispatch( StdDispatchMethod::PREDESTRUCTOR, nullstream, 0 );
    return dispatch( StdDispatchMethod::DESTRUCTOR, nullstream, 0 );
}

////////////////////////////////////////////////////////////////////////////////
opcd CoidNode::dpx_spawn( bool crit )
{
    spawn_thread( __spawn, this, crit, "spawn" );
    return 0;
}



void* CoidNode::__spawn( void* x )
{
    opcd e = ((CoidNode*)x)->dispatch( StdDispatchMethod::SPAWN, nullstream, 0 );
    return x;
}

////////////////////////////////////////////////////////////////////////////////
opcd CoidNode::dpx_authentify( binstream& arg, metastream* meta )
{
    NodeClass nc = GET_VIRTUAL_INTERFACE();
    if( !nc.is_of_type( ServiceInstanceCoid::GET_INTERFACE() ) )
        return ersUNAVAILABLE "operation can be performed only on ServiceInstanceCoid objects";

    return dispatch( StdDispatchMethod::AUTHENTIFY, arg, meta );
}

////////////////////////////////////////////////////////////////////////////////
opcd CoidNode::dpx_authentify_shared( binstream& arg, metastream* meta )
{
    NodeClass nc = GET_VIRTUAL_INTERFACE();
    if( !nc.is_of_type( ServiceInstanceCoid::GET_INTERFACE() ) )
        return ersUNAVAILABLE "operation can be performed only on ServiceInstanceCoid objects";

    return dispatch( StdDispatchMethod::AUTHENTIFY_SHARED, arg, meta );
}

////////////////////////////////////////////////////////////////////////////////
opcd CoidNode::dpx_authentify_auto()
{
    NodeClass nc = GET_VIRTUAL_INTERFACE();
    if( !nc.is_of_type( ServiceInstanceCoid::GET_INTERFACE() ) )
        return ersUNAVAILABLE "operation can be performed only on ServiceInstanceCoid objects";

    return dispatch( StdDispatchMethod::AUTHENTIFY_AUTO, nullstream, 0 );
}

////////////////////////////////////////////////////////////////////////////////
opcd CoidNode::dpx_create_interface( const CreateInterfaceInfo& cinfo, binstream& ret )
{
    NodeClass nc = GET_VIRTUAL_INTERFACE();
    if( !nc.is_of_type( ServiceInstanceCoid::GET_INTERFACE() ) )
        return ersUNAVAILABLE "operation can be performed only on ServiceInstanceCoid objects";

    binstreambuf buf;
    buf.write_ptr(&cinfo);

    inoutstream iox( &buf, &ret );
    opcd e = dispatch( StdDispatchMethod::CREATE_INTERFACE, iox, 0 );
    if(!e)
        e = ((ServiceInstanceCoid*)this)->connect_interface( cinfo._ifacethis );

    return e;
}

////////////////////////////////////////////////////////////////////////////////
opcd CoidNode::dpx_dispatch_loop( netSocket* ns, uint port )
{
    NodeClass nc = GET_VIRTUAL_INTERFACE();
    if( !nc.is_of_type( ServiceInstanceCoid::GET_INTERFACE() ) )
        return ersUNAVAILABLE "operation can be performed only on ServiceInstanceCoid objects";

    binstreambuf buf;
    buf.write_ptr(ns);
    buf << port;

    opcd e = dispatch( StdDispatchMethod::HANDLE_NET_LOOP, buf, 0 );
    return e;
}

////////////////////////////////////////////////////////////////////////////////
opcd CoidNode::dpx_command( const charstr& cmd, binstream& rply )
{
    binstreambuf pin;
    pin << (ints)cmd.ptr();
    inoutstream iox( &pin, &rply );
    return dispatch( StdDispatchMethod::COMMAND, iox, 0 );
}

////////////////////////////////////////////////////////////////////////////////
opcd CoidNode::dpx_echo( binstream& rply )
{
    return dispatch( StdDispatchMethod::ECHO, rply, 0 );
}


////////////////////////////////////////////////////////////////////////////////
opcd CoidNode::dispatch_method( uint id, const token& method, binstream& parinout, metastream* meta )
{
    NODEREF<CoidNode> n;

    opcd e = SRVROOTA._objmgr.get_node( id, n );
    if(e) return e;

    GUARD g( (CoidNode*)n );
    CoidNode* obj = n;

    uint mid;
    if( method[0] == '.' )
    {
        token t = method;
        ++t;
        mid = obj->get_method_id(t);
        if( mid == UMAX32 )
            return ersUNKNOWN_CMD;
    }
    else
        mid = StdDispatchMethod::COMMAND;

    try {
        if( mid == StdDispatchMethod::COMMAND )
        {
            charstr txtt;
            txtstream io(parinout);
            io >> txtt >> BINSTREAM_ACK;
            command_tokens cts(txtt);

            if( method == "help" )
                return obj->stream_cmds_list( cts[0], io );
            else
                return obj->dispatch_cmd( method, cts, io );
        }
        else
            return obj->dispatch( mid, parinout, meta );
    }
    catch( thread::Exception& ) { throw; }
    catch( opcd cd )
    {
        SRVROOTA.post_server_msg(LOGMSG::TYPE_ERROR,0) << "Error: "
            << _id << " (class: " << obj->class_name() << "): " << opcd_formatter(cd) << EOM;
    }
    catch(...)
    {
        SRVROOTA.post_server_msg(LOGMSG::TYPE_ALERT,0) << "Exception in working thread of object "
            << _id << " (class: " << obj->class_name() << ")" << EOM;
    }

    return 0;
}

////////////////////////////////////////////////////////////////////////////////
opcd CoidNode::list_cmds( const token& cmd, dynarray<charstr>& cmds ) const
{
    if( cmd == "id" )
        cmds.push("id\n\tprint object id");
    else if( cmd == "cid" )
        cmds.push("cid\n\tprint id of connection owner");
    else if( cmd == "info" )
        cmds.push("info\n\tprint name and class name of the object");
    else if( cmd == "lot" )
        cmds.push("lot\n\tlist object threads");
    else if( cmd == "lodl" )
        cmds.push("lodl\n\tlist object detach list; objects that would be destroyed if this object is dropped");
    else if( cmd == "parid" )
        cmds.push("parid\n\treturn parent's object id");
    else if( cmd == "rip" )
        cmds.push("rip\n\tprint remote IP address");
#ifdef _DEBUG
    else if( cmd == "lmx" )
        cmds.push("lmx\n\tlist locked mutexes (time in seconds may be given as argument, so only those locked for specified amount of time will be listed)");
#endif
    else if( cmd.is_empty() )
    {
        *cmds.add() = "help";
        *cmds.add() = "cid";
        *cmds.add() = "id";
        *cmds.add() = "info";
        *cmds.add() = "lodl";
        *cmds.add() = "lot";
        *cmds.add() = "parid";
        *cmds.add() = "rip";
#ifdef _DEBUG
        *cmds.add() = "lmx";
#endif
    }

    return 0;
}

////////////////////////////////////////////////////////////////////////////////
opcd CoidNode::dispatch_cmd( const token& cmd, command_tokens& arg, txtstream& reply )
{
    if( cmd.is_empty()  ||  cmd == "help" )
        return stream_cmds_list( cmd, reply );
    else if( cmd == "id" )
    {
        reply << _id;
        return 0;
    }
    else if( cmd == "cid" )
    {
        NODEREF<ConnectionCoid> ref;
        opcd e = SRVROOTA._objmgr.get_referred( _id, ref );
        if(!e)
            reply << ref->get_id();
        return e;
    }
    else if( cmd == "parid" )
    {
        uint sup = UMAX32;
        if( 0 == SRVROOTA._objmgr.get_superior( _id, &sup ) )
            reply << sup;
        return 0;
    }
    else if( cmd == "info" )
    {
        reply << "object " << _id;

        TreeMgr::Attribs att;
        if( 0 == SRVROOTA._objmgr.get_node_attribs( _id, att ) )
             reply << ": [" << att.nc.get_class_name() << "] " << att.name
             << ((att.flags & fSIGNAL_EXIT) ? " fSIGNAL_EXIT" : "")
             << ((att.flags & fREJECT_ATTACH) ? " fREJECT_ATTACH" : "");

        reply << "\n";
        return 0;
    }
    else if( cmd == "lot" )
    {
        dynarray<thread> thr_own;
        dynarray<thread> thr_using;
        opcd e = SRVROOTA._objmgr.get_thread_list( _id, thr_own, thr_using );
        if(e)
            return e;

        if( thr_own.size() > 0 )
            reply << "-->  owned threads:\n";

        thread_info ti;
        {for( uint i=0; i<thr_own.size(); ++i )
        {
            if( 0 == SRVROOTA._thrmgr.get_thread_info( thr_own[i], ti ) )
            {
                if( ti._tempid != _id )
                {
                    reply << "thread " << thr_own[i] << " '" << ti._name << "' currently lurking in object " << ti._tempid;

                    TreeMgr::Attribs att;
                    if( 0 == SRVROOTA._objmgr.get_node_attribs( ti._tempid, att ) )
                         reply << ": [" << att.nc.get_class_name() << "] " << att.name;
                    else
                        reply << ": unknown";
                    reply << "\n";
                }
                else
                    reply << "thread " << thr_own[i] << " '" << ti._name << "'\n";
            }
        }}

        if( thr_using.size() > 0 )
            reply << "-->  threads using the object:\n";

        {for( uint i=0; i<thr_using.size(); ++i )
        {
            if( 0 == SRVROOTA._thrmgr.get_thread_info( thr_using[i], ti ) )
            {
                reply << "thread " << thr_using[i] << " '" << ti._name << "' of object " << ti._oid;

                TreeMgr::Attribs att;
                if( 0 == SRVROOTA._objmgr.get_node_attribs( ti._oid, att ) )
                     reply << ": [" << att.nc.get_class_name() << "] " << att.name;

                reply << "\n";
            }
        }}

        return 0;
    }
    else if( cmd == "lodl" )
    {
        dynarray<uint> dlo;
        opcd e = SRVROOTA._objmgr.get_detach_list( _id, dlo );
        if(e)
            return e;

        {for( uint i=0; i<dlo.size(); ++i )
        {
            reply << "object " << dlo[i];

            TreeMgr::Attribs att;
            if( 0 == SRVROOTA._objmgr.get_node_attribs( dlo[i], att ) )
                 reply << ": [" << att.nc.get_class_name() << "] " << att.name;

            reply << "\n";
        }}

        return 0;
    }
    else if( cmd == "mxc" )
    {
        reply << "mutex counter: " << _mutex.counter();
        return 0;
    }
    else if( cmd == "rip" )
    {
        NODEREF<ConnectionCoid> ref;
        opcd e = SRVROOTA._objmgr.get_referred( _id, ref );
		ConnectionCoid * c = ref.ptr();
		if( e || c == NULL ) return ersNOT_FOUND;

        charstr host;
		if( c->_client_addr.isLocalHost() ) {
			netAddress::getLocalHostName( host );
			host << ":" << c->_client_addr.getPort();
		}
        else c->_client_addr.getHostName( host, true );
        reply << host;
		return 0;
    }
#ifdef _DEBUG
    else if( cmd == "lmx" )
    {
		uint time = 0;
		if( arg._cmd.len() )
			time = arg._cmd.touint();
		charstr out;
		opcd e = create_list( out, time, false );
		if( out.len() )
			reply.xwrite_raw( out.ptr(), out.len() );
		return e;
    }
#endif
    else
        return ersUNKNOWN_CMD;
}


} // namespace coid

